
<template>
  <div>
    <input type="file" @change="handleFileChange" />
    <el-button @click="handleUpload">upload</el-button>
    <h2>{{ uploadPercentage }}</h2>
    <span>{{ data }}</span>
  </div>
</template>

<script>
import axios from 'axios';

function request({
  url,
  method = "post",
  data,
  headers = {},
  onProgress = e => e,
  requestList
}) {
  return new Promise(resolve => {
    const xhr = new XMLHttpRequest();
    xhr.upload.onprogress = onProgress;
    xhr.open(method, url);
    Object.keys(headers).forEach(key =>
      xhr.setRequestHeader(key, headers[key])
    );

    xhr.send(data);
    xhr.onload = e => {
      resolve({
        data: e.target.response
      });
    };
  });
}



// 切片大小
// the chunk size
const SIZE = 10 * 1024 * 1024;

export default {
  data: () => ({
    container: {
      file: null,
    },
    data: [] // 存放的是 promise 数组
  }),
  computed: {
    uploadPercentage() {
      if (!this.container.file || !this.data.length) return 0;
      const loaded = this.data
        .map(item => item.size * item.percentage)
        .reduce((acc, cur) => acc + cur);
      let cur = parseInt((loaded / this.container.file.size).toFixed(2))
      return cur > 100 ? 100 : cur;
    }
  },

  methods: {
    /**
     *  监听文件上传 input
     * @param {*} e
     */
    handleFileChange(e) {
      const [file] = e.target.files;
      if (!file) return;
      Object.assign(this.$data, this.$options.data());
      this.container.file = file;
    },

    // 生成文件切片
    createFileChunk(file, size = SIZE) {
      const fileChunkList = [];
      let cur = 0;
      while (cur < file.size) {
        fileChunkList.push({ file: file.slice(cur, cur + size) });
        cur += size;
      }

      return fileChunkList;
    },
    /**
     * 文件上传
     */
    async handleUpload() {
      if (!this.container.file) return;
      const fileChunkList = this.createFileChunk(this.container.file);
      this.data = fileChunkList.map(({ file }, index) => ({
        chunk: file,
        index,
        // 文件名 + 数组下标
        size: SIZE,
        hash: this.container.file.name + "-" + index,
        percentage: 0
      }));
      await this.uploadChunks();
    },
    /**
     * 创建文件切片
     */
    async uploadChunks() {
      const requestList = this.data
        .map(({ chunk, hash, index }) => {
          const formData = new FormData();
          formData.append("chunk", chunk);
          formData.append("hash", hash);
          formData.append("filename", this.container.file.name);
          return { formData, index };
        })
        .map(({ formData, index }) =>
          // axios.post('http://localhost:3000', formData, { headers: { 'Content-Type': 'multipart/form-data' } })
          request({
            url: "http://localhost:3000",
            data: formData,
            onProgress: this.createProgressHandler(this.data[index]),
          })
        );
      // 并发请求
      const data = await Promise.all(requestList);
      console.log(data);
      // 合并切片
      await this.mergeRequest();
    },
    /**
     * 文件合并
     */
    async mergeRequest() {

      await request({
        url: "http://localhost:3000/merge",
        headers: {
          "content-type": "application/json"
        },
        data: JSON.stringify({
          size: SIZE,
          filename: this.container.file.name
        })
      });


      // await axios.post('http://localhost:3000/merge', JSON.stringify({
      //   size: SIZE,
      //   filename: this.container.file.name
      // }))


    },
    /**
     * 进度条
     * @param {*} item
     */
    createProgressHandler(item) {
      return e => {
        item.percentage = parseInt(String((e.loaded / e.total) * 100));
      };
    },
    /**
     * 创建hash
     * @param {*} fileChunkList
     */
    calculateHash(fileChunkList) {
      return new Promise(resolve => {
        // 添加 worker 属性
        this.container.worker = new Worker("./hash.js");
        this.container.worker.postMessage({ fileChunkList });
        this.container.worker.onmessage = e => {
          const { percentage, hash } = e.data;
          this.hashPercentage = percentage;
          if (hash) {
            resolve(hash);
          }
        };
      });
    }
  }
};

</script>
